#include "../IpSocket.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

// TODO: move this to general part
#include <string.h>

using namespace Greatewall::Interface;

namespace Greatewall
{
    namespace Network
    {
        IpSocket::IpSocket(SocketEventMonitor& monitor, SysSockHandler hdl)
            : SystemSocket(monitor, hdl)
        {
            memset(aligned_buf, 0, sizeof(aligned_buf));
        }

        bool
        IpSocket::send(const char* buf, ssize_t& size, bool non_block)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_hdl < 0)
            {
                // TODO: Error handling
                return false;
            }

            if (buf && size <= 0)
            {
                // TODO: Error handling
                return false;
            }

            if (buf && size > 0)
            {
                ssize_t sent = ::send(_hdl, buf, size, non_block ? MSG_DONTWAIT : 0);

                if (sent == -1)
                {
                    // TODO: Error handling
                    size = sent;
                    return false;
                }

                // push the rest of content to queue
                if (sent < size)
                {
                    char* qbuf = new char(size - sent);
                    memcpy(qbuf, buf + sent, size - sent);
                    _outbound_queue.push(OutboundNode(qbuf, size - sent));
                    size = sent;
                }
            }
            else
            if (_outbound_queue.size())
            {
                OutboundNode& qbuf = _outbound_queue.front();

                if (qbuf._buf && qbuf._size && qbuf._size > qbuf._offset)
                {
                    ssize_t sent = ::send(_hdl, qbuf._buf + qbuf._offset, qbuf._size - qbuf._offset, MSG_DONTWAIT);

                    if (sent == -1)
                    {
                        _outbound_queue.pop();
                        // TODO: Error handling
                        size = sent;
                        return false;
                    }

                    qbuf._offset += sent;
                    if (qbuf._offset >= qbuf._size)
                        _outbound_queue.pop();

                    size = sent;
                }
                else
                    _outbound_queue.pop();
            }
            return true;
        }

        bool
        IpSocket::recv(char*& buf, ssize_t& size, bool non_block)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_hdl < 0)
            {
                // TODO: Error handling
                return false;
            }

            if (buf && size <= 0)
            {
                // TODO: Error handling
                return false;
            }

            // determine which buffer we are going to use
            char*   rbuf  = buf ? buf  : (char*)aligned_buf;
            ssize_t rsize = buf ? size : sizeof(aligned_buf);

            size = ::recv(_hdl, rbuf, rsize, non_block ? MSG_DONTWAIT : 0);

            if (size == -1)
            {
                // Error handling
                return false;
            }

            // Return the static buffer, if read buffer is not provided
            if (!buf) buf = rbuf;

            return true;
        }
    }
}
